#pragma once

#include <vector>
#include <utility>

#include "torrent/fwd_decl.hpp"

namespace torrent {

class PieceSelectBase
{
public:
	static bool isDownloading(SessionList& sessions, size_t pieceIdx);
};

class Piece1MinFirst : public PieceSelectBase
{
public:
	typedef int ResultType;
	const static ResultType INVALID_PIECE_IDX = -1;
	static inline bool invalid(ResultType& rs) { return INVALID_PIECE_IDX==rs; }
	static ResultType get(Bitfield& local, SessionList& sessions, P2PSessionPtr curSession, const size_t max = 1);
	static inline size_t get1Index(ResultType& rs) { return static_cast<size_t>(rs); }
};

class Piece1Random : public PieceSelectBase
{
public:
	typedef int ResultType;
	const static ResultType INVALID_PIECE_IDX = -1;
	static inline bool invalid(ResultType& rs) { return INVALID_PIECE_IDX==rs; }
	static ResultType get(Bitfield& local, SessionList& sessions, P2PSessionPtr curSession, const size_t max = 1);
	static inline size_t get1Index(ResultType& rs) { return static_cast<size_t>(rs); }
};

class PiecesNMinFirst : public PieceSelectBase
{
public:
	typedef SessionPieceList ResultType;
	static inline bool invalid(ResultType& rs) { return rs.empty(); }
	static ResultType get(Bitfield& local, SessionList& sessions, P2PSessionPtr curSession, const size_t max = 1);
	static inline size_t get1Index(ResultType& rs) { return rs.front().second; }
};

template<class SelectAlgorithm>
class PieceSelector
{
public:
	typedef typename SelectAlgorithm::ResultType ResultType;

	static inline ResultType get(Bitfield& local, SessionList& sessions, P2PSessionPtr curSession, const size_t max = 1)
	{
		return SelectAlgorithm::get(local, sessions, curSession, max);
	}
	static inline bool invalid(ResultType& ret) { return SelectAlgorithm::invalid(ret); }
	static inline size_t get1Index(ResultType& rs) { return SelectAlgorithm::get1Index(rs); }
};

typedef PieceSelector<Piece1MinFirst> MySelector;

}
